home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / newsgrp / group99a.txt / 000124_icon-group-sender _Fri Jun 4 14:45:35 1999.msg < prev    next >
Internet Message Format  |  2000-09-20  |  5KB

  1. Return-Path: <icon-group-sender>
  2. Received: (from root@localhost)
  3.     by baskerville.CS.Arizona.EDU (8.9.1a/8.9.1) id OAA03881
  4.     for icon-group-addresses; Fri, 4 Jun 1999 14:41:55 -0700 (MST)
  5. Message-Id: <199906042141.OAA03881@baskerville.CS.Arizona.EDU>
  6. Delivered-To: icon-group@silliac.cs.arizona.edu
  7. Date: Fri, 04 Jun 1999 09:09:29 -0700
  8. From: Steve Wampler <swampler@noao.edu>
  9. X-Accept-Language: en
  10. To: icon-group@optima.CS.Arizona.EDU
  11. Subject: Re: Find the longest matching substrings
  12. Errors-To: icon-group-errors@optima.CS.Arizona.EDU
  13. Status: RO
  14.  
  15. This is a multi-part message in MIME format.
  16. --------------09EB4280837648B91A00FBDF
  17. Content-Type: text/plain; charset=us-ascii
  18. Content-Transfer-Encoding: 7bit
  19.  
  20. gep2@terabites.com wrote:
  21. > > I am working on a project that will mark the inserted or deleted phases
  22. > between two strings.  I am wondering whether you know any library
  23. > procedure that will anchor ( or report ) the longest phases that exists
  24. > in both  strings.
  25. > > For example, assume that we have two strings:
  26. >     old:="other strings ab cd e and more substrings"
  27. >     new:="some more substrings ab cd ab cd e"
  28. > > This function should return phases and position in both strings, i.e.
  29. > [15,  28, "ab cd e"]  for "ab cd e" is the longest phase that matched (
  30. > it has three continuous matching elements ).
  31. > If I were going to try to make something like this FAST (and especially if one
  32. > of the strings were relatively constant) I'd probably want to use a hash (or
  33. > even direct index?) table pre-computed with the offsets of character substrings
  34. > (I'd probably use two-character adjacent pairs) in one of those strings;  this
  35. > would enable stepping rapidly through the second string and quickly finding at
  36. > least (only just the) plausible candidates for launching more detailed substring
  37. > compare (and length computation) operations.  This kind of thing would likely be
  38. > storage-hungry, but that's less of a concern today than it once was.
  39.  
  40. Taking Gordon's comment on speed to heart, here's an adaptation of the
  41. previous solution that is faster (uses simple heuristics to avoid
  42. looking at phrases that can't possibly be longer than ones that are
  43. found already).  Note that it can probably be sped up even more by
  44. improving
  45. these heuristics a bit.
  46.  
  47. This version is about 2.5x faster on my test case (the "that that is..."
  48. nonsense).  Applying this adaptation to my solution that ignores
  49. punctuation
  50. when comparing phrases results in a speedup of about 25x!  (Because the
  51. test case strings match each other when ignoring punctuation.)
  52.  
  53. Oh, I also modified the test driver to do time comparisions.
  54.  
  55. Incidently, it would be 'better' if the phrase element procedures were
  56. converted into string scanning procedures.  Scanning strings for phrase
  57. elements sounds useful to me and can considered as a generalization
  58. of scanning strings for characters - in fact, the code here is just
  59. an adaptation of code to find the longest common substring.
  60.  
  61. It would also be better if the code were rewritten to accept different
  62. definitions of a phrase element more easily!
  63.  
  64. --
  65. Steve Wampler-  SOLIS Project, National Solar Observatory
  66. swampler@noao.edu
  67. --------------09EB4280837648B91A00FBDF
  68. Content-Type: text/plain; charset=us-ascii;
  69.  name="phrase3.icn"
  70. Content-Disposition: inline;
  71.  filename="phrase3.icn"
  72. Content-Transfer-Encoding: 7bit
  73.  
  74. procedure main(args)
  75.  
  76.     n := args[1]|10
  77.     start := &time
  78.     every 1 to n do {
  79.         result := longPhrase(read(),read())
  80.         }
  81.     len := &time - start
  82.     write("[",result[1],",",result[2],",\"",result[3],"\"]")
  83.     write(real(len)/n,"ms per loop for ",n," loops")
  84.  
  85. end
  86.  
  87. # Find longest phrase common to s1 and s2.  Fails if no common phrase.
  88. #
  89. #  Assumes simple definition for a phrase (sequence of letters)
  90. #  Assumes punctuation is to be considered when matching phrases
  91. #
  92. procedure longPhrase(s1,s2)
  93.     static pChars
  94.     initial pChars := &letters        # chars legal in phrase element
  95.  
  96.     maxlen := 0
  97.  
  98.     every i := pStart(s1, pChars) do {
  99.         every k := rpStop(s1, i, pChars) do {
  100.             if j := findPhrase(s1[i:k], s2, pChars) then {
  101.                 if maxlen <:= numPhrases(s1[i:k], pChars) then {
  102.                     saveStart1 := i
  103.                     saveStop1  := k
  104.                     saveStart2 := j
  105.                     }
  106.                 break            # no need to find shorter phrases
  107.                 }            #   that start here
  108.             }
  109.  
  110.         }
  111.  
  112.     return [\saveStart1, saveStart2, s1[saveStart1:saveStop1]]
  113. end
  114.  
  115. procedure pStart(s, pc)            # generate start of all phrases in s
  116.     local i
  117.  
  118.     s ? {
  119.         while tab(i := upto(pc)) do {
  120.             tab(many(pc))
  121.             suspend i
  122.             }
  123.         }
  124. end
  125.  
  126. procedure rpStop(s, i, pc)        # generates end of all phrases in s,
  127.                                         #   starting from right of string
  128.     suspend *s+2 - pStart(reverse(s[i:0]), pc)
  129. end
  130.  
  131. procedure numPhrases(s, pc)        # Count number of phrases in s
  132.     local count
  133.  
  134.     count := 0
  135.     every pStart(s,pc) do count +:= 1
  136.  
  137.     return count
  138. end
  139.  
  140. procedure findPhrase(p, s, pc)        # find phrase p in s
  141.     local i
  142.  
  143.     s ? {
  144.         while tab(i := upto(pc)) do {
  145.             if match(p) then suspend i
  146.             tab(many(pc))
  147.             }
  148.         }
  149. end
  150.  
  151. --------------09EB4280837648B91A00FBDF--
  152.  
  153.